home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / util / Hashtable.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  28.0 KB  |  935 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)Hashtable.java    1.70 98/09/30
  3.  *
  4.  * Copyright 1994-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util;
  16. import java.io.*;
  17.  
  18. /**
  19.  * This class implements a hashtable, which maps keys to values. Any 
  20.  * non-<code>null</code> object can be used as a key or as a value. <p>
  21.  *
  22.  * To successfully store and retrieve objects from a hashtable, the 
  23.  * objects used as keys must implement the <code>hashCode</code> 
  24.  * method and the <code>equals</code> method. <p>
  25.  *
  26.  * An instance of <code>Hashtable</code> has two parameters that affect its
  27.  * performance: <i>initial capacity</i> and <i>load factor</i>.  The
  28.  * <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the
  29.  * <i>initial capacity</i> is simply the capacity at the time the hash table
  30.  * is created.  Note that the hash table is <i>open</i>: in the case a "hash
  31.  * collision", a single bucket stores multiple entries, which must be searched
  32.  * sequentially.  The <i>load factor</i> is a measure of how full the hash
  33.  * table is allowed to get before its capacity is automatically increased.
  34.  * When the number of entries in the hashtable exceeds the product of the load
  35.  * factor and the current capacity, the capacity is increased by calling the
  36.  * <code>rehash</code> method.<p>
  37.  *
  38.  * Generally, the default load factor (.75) offers a good tradeoff between
  39.  * time and space costs.  Higher values decrease the space overhead but
  40.  * increase the time cost to look up an entry (which is reflected in most
  41.  * <tt>Hashtable</tt> operations, including <tt>get</tt> and <tt>put</tt>).<p>
  42.  *
  43.  * The initial capacity controls a tradeoff between wasted space and the
  44.  * need for <code>rehash</code> operations, which are time-consuming.
  45.  * No <code>rehash</code> operations will <i>ever</i> occur if the initial
  46.  * capacity is greater than the maximum number of entries the
  47.  * <tt>Hashtable</tt> will contain divided by its load factor.  However,
  48.  * setting the initial capacity too high can waste space.<p>
  49.  *
  50.  * If many entries are to be made into a <code>Hashtable</code>, 
  51.  * creating it with a sufficiently large capacity may allow the 
  52.  * entries to be inserted more efficiently than letting it perform 
  53.  * automatic rehashing as needed to grow the table. <p>
  54.  *
  55.  * This example creates a hashtable of numbers. It uses the names of 
  56.  * the numbers as keys:
  57.  * <p><blockquote><pre>
  58.  *     Hashtable numbers = new Hashtable();
  59.  *     numbers.put("one", new Integer(1));
  60.  *     numbers.put("two", new Integer(2));
  61.  *     numbers.put("three", new Integer(3));
  62.  * </pre></blockquote>
  63.  * <p>
  64.  * To retrieve a number, use the following code: 
  65.  * <p><blockquote><pre>
  66.  *     Integer n = (Integer)numbers.get("two");
  67.  *     if (n != null) {
  68.  *         System.out.println("two = " + n);
  69.  *     }
  70.  * </pre></blockquote>
  71.  * <p>
  72.  * As of JDK1.2, this class has been retrofitted to implement Map,
  73.  * so that it becomes a part of Java's collection framework.  Unlike
  74.  * the new collection implementations, Hashtable is synchronized.<p>
  75.  *
  76.  * The Iterators returned by the iterator and listIterator methods
  77.  * of the Collections returned by all of Hashtable's "collection view methods"
  78.  * are <em>fail-fast</em>: if the Hashtable is structurally modified
  79.  * at any time after the Iterator is created, in any way except through the
  80.  * Iterator's own remove or add methods, the Iterator will throw a
  81.  * ConcurrentModificationException.  Thus, in the face of concurrent
  82.  * modification, the Iterator fails quickly and cleanly, rather than risking
  83.  * arbitrary, non-deterministic behavior at an undetermined time in the future.
  84.  * The Enumerations returned by Hashtable's keys and values methods are
  85.  * <em>not</em> fail-fast.
  86.  *
  87.  * @author  Arthur van Hoff
  88.  * @author  Josh Bloch
  89.  * @version 1.70, 09/30/98
  90.  * @see     Object#equals(java.lang.Object)
  91.  * @see     Object#hashCode()
  92.  * @see     Hashtable#rehash()
  93.  * @see     Collection
  94.  * @see        Map
  95.  * @see        HashMap
  96.  * @see        TreeMap
  97.  * @since JDK1.0
  98.  */
  99. public class Hashtable extends Dictionary implements Map, Cloneable,
  100.                              java.io.Serializable {
  101.     /**
  102.      * The hash table data.
  103.      */
  104.     private transient Entry table[];
  105.  
  106.     /**
  107.      * The total number of entries in the hash table.
  108.      */
  109.     private transient int count;
  110.  
  111.     /**
  112.      * The table is rehashed when its size exceeds this threshold.  (The
  113.      * value of this field is (int)(capacity * loadFactor).)
  114.      *
  115.      * @serial
  116.      */
  117.     private int threshold;
  118.  
  119.     /**
  120.      * The load factor for the hashtable.
  121.      *
  122.      * @serial
  123.      */
  124.     private float loadFactor;
  125.  
  126.     /**
  127.      * The number of times this Hashtable has been structurally modified
  128.      * Structural modifications are those that change the number of entries in
  129.      * the Hashtable or otherwise modify its internal structure (e.g.,
  130.      * rehash).  This field is used to make iterators on Collection-views of
  131.      * the Hashtable fail-fast.  (See ConcurrentModificationException).
  132.      */
  133.     private transient int modCount = 0;
  134.  
  135.     /** use serialVersionUID from JDK 1.0.2 for interoperability */
  136.     private static final long serialVersionUID = 1421746759512286392L;
  137.  
  138.     /**
  139.      * Constructs a new, empty hashtable with the specified initial 
  140.      * capacity and the specified load factor.
  141.      *
  142.      * @param      initialCapacity   the initial capacity of the hashtable.
  143.      * @param      loadFactor        the load factor of the hashtable.
  144.      * @exception  IllegalArgumentException  if the initial capacity is less
  145.      *             than zero, or if the load factor is nonpositive.
  146.      */
  147.     public Hashtable(int initialCapacity, float loadFactor) {
  148.     if (initialCapacity < 0)
  149.         throw new IllegalArgumentException("Illegal Capacity: "+
  150.                                                initialCapacity);
  151.         if (loadFactor <= 0)
  152.             throw new IllegalArgumentException("Illegal Load: "+loadFactor);
  153.  
  154.         if (initialCapacity==0)
  155.             initialCapacity = 1;
  156.     this.loadFactor = loadFactor;
  157.     table = new Entry[initialCapacity];
  158.     threshold = (int)(initialCapacity * loadFactor);
  159.     }
  160.  
  161.     /**
  162.      * Constructs a new, empty hashtable with the specified initial capacity
  163.      * and default load factor, which is <tt>0.75</tt>.
  164.      *
  165.      * @param     initialCapacity   the initial capacity of the hashtable.
  166.      * @exception IllegalArgumentException if the initial capacity is less
  167.      *              than zero.
  168.      */
  169.     public Hashtable(int initialCapacity) {
  170.     this(initialCapacity, 0.75f);
  171.     }
  172.  
  173.     /**
  174.      * Constructs a new, empty hashtable with a default capacity and load
  175.      * factor, which is <tt>0.75</tt>. 
  176.      */
  177.     public Hashtable() {
  178.     this(101, 0.75f);
  179.     }
  180.  
  181.     /**
  182.      * Constructs a new hashtable with the same mappings as the given 
  183.      * Map.  The hashtable is created with a capacity of twice the number
  184.      * of entries in the given Map or 11 (whichever is greater), and a
  185.      * default load factor, which is <tt>0.75</tt>.
  186.      *
  187.      * @since   JDK1.2
  188.      */
  189.     public Hashtable(Map t) {
  190.     this(Math.max(2*t.size(), 11), 0.75f);
  191.     putAll(t);
  192.     }
  193.  
  194.     /**
  195.      * Returns the number of keys in this hashtable.
  196.      *
  197.      * @return  the number of keys in this hashtable.
  198.      */
  199.     public int size() {
  200.     return count;
  201.     }
  202.  
  203.     /**
  204.      * Tests if this hashtable maps no keys to values.
  205.      *
  206.      * @return  <code>true</code> if this hashtable maps no keys to values;
  207.      *          <code>false</code> otherwise.
  208.      */
  209.     public boolean isEmpty() {
  210.     return count == 0;
  211.     }
  212.  
  213.     /**
  214.      * Returns an enumeration of the keys in this hashtable.
  215.      *
  216.      * @return  an enumeration of the keys in this hashtable.
  217.      * @see     Enumeration
  218.      * @see     #elements()
  219.      * @see    #keySet()
  220.      * @see    Map
  221.      */
  222.     public synchronized Enumeration keys() {
  223.     return new Enumerator(KEYS, false);
  224.     }
  225.  
  226.     /**
  227.      * Returns an enumeration of the values in this hashtable.
  228.      * Use the Enumeration methods on the returned object to fetch the elements
  229.      * sequentially.
  230.      *
  231.      * @return  an enumeration of the values in this hashtable.
  232.      * @see     java.util.Enumeration
  233.      * @see     #keys()
  234.      * @see    #values()
  235.      * @see    Map
  236.      */
  237.     public synchronized Enumeration elements() {
  238.     return new Enumerator(VALUES, false);
  239.     }
  240.  
  241.     /**
  242.      * Tests if some key maps into the specified value in this hashtable.
  243.      * This operation is more expensive than the <code>containsKey</code>
  244.      * method.<p>
  245.      *
  246.      * Note that this method is identical in functionality to containsValue,
  247.      * (which is part of the Map interface in the collections framework).
  248.      * 
  249.      * @param      value   a value to search for.
  250.      * @return     <code>true</code> if and only if some key maps to the
  251.      *             <code>value</code> argument in this hashtable as 
  252.      *             determined by the <tt>equals</tt> method;
  253.      *             <code>false</code> otherwise.
  254.      * @exception  NullPointerException  if the value is <code>null</code>.
  255.      * @see        #containsKey(Object)
  256.      * @see        #containsValue(Object)
  257.      * @see       Map
  258.      */
  259.     public synchronized boolean contains(Object value) {
  260.     if (value == null) {
  261.         throw new NullPointerException();
  262.     }
  263.  
  264.     Entry tab[] = table;
  265.     for (int i = tab.length ; i-- > 0 ;) {
  266.         for (Entry e = tab[i] ; e != null ; e = e.next) {
  267.         if (e.value.equals(value)) {
  268.             return true;
  269.         }
  270.         }
  271.     }
  272.     return false;
  273.     }
  274.  
  275.     /**
  276.      * Returns true if this Hashtable maps one or more keys to this value.<p>
  277.      *
  278.      * Note that this method is identical in functionality to contains
  279.      * (which predates the Map interface).
  280.      *
  281.      * @param value value whose presence in this Hashtable is to be tested.
  282.      * @see       Map
  283.      * @since JDK1.2
  284.      */
  285.     public boolean containsValue(Object value) {
  286.     return contains(value);
  287.     }
  288.  
  289.     /**
  290.      * Tests if the specified object is a key in this hashtable.
  291.      * 
  292.      * @param   key   possible key.
  293.      * @return  <code>true</code> if and only if the specified object 
  294.      *          is a key in this hashtable, as determined by the 
  295.      *          <tt>equals</tt> method; <code>false</code> otherwise.
  296.      * @see     #contains(Object)
  297.      */
  298.     public synchronized boolean containsKey(Object key) {
  299.     Entry tab[] = table;
  300.     int hash = key.hashCode();
  301.     int index = (hash & 0x7FFFFFFF) % tab.length;
  302.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  303.         if ((e.hash == hash) && e.key.equals(key)) {
  304.         return true;
  305.         }
  306.     }
  307.     return false;
  308.     }
  309.  
  310.     /**
  311.      * Returns the value to which the specified key is mapped in this hashtable.
  312.      *
  313.      * @param   key   a key in the hashtable.
  314.      * @return  the value to which the key is mapped in this hashtable;
  315.      *          <code>null</code> if the key is not mapped to any value in
  316.      *          this hashtable.
  317.      * @see     #put(Object, Object)
  318.      */
  319.     public synchronized Object get(Object key) {
  320.     Entry tab[] = table;
  321.     int hash = key.hashCode();
  322.     int index = (hash & 0x7FFFFFFF) % tab.length;
  323.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  324.         if ((e.hash == hash) && e.key.equals(key)) {
  325.         return e.value;
  326.         }
  327.     }
  328.     return null;
  329.     }
  330.  
  331.     /**
  332.      * Increases the capacity of and internally reorganizes this 
  333.      * hashtable, in order to accommodate and access its entries more 
  334.      * efficiently.  This method is called automatically when the 
  335.      * number of keys in the hashtable exceeds this hashtable's capacity 
  336.      * and load factor. 
  337.      */
  338.     protected void rehash() {
  339.     int oldCapacity = table.length;
  340.     Entry oldMap[] = table;
  341.  
  342.     int newCapacity = oldCapacity * 2 + 1;
  343.     Entry newMap[] = new Entry[newCapacity];
  344.  
  345.     modCount++;
  346.     threshold = (int)(newCapacity * loadFactor);
  347.     table = newMap;
  348.  
  349.     for (int i = oldCapacity ; i-- > 0 ;) {
  350.         for (Entry old = oldMap[i] ; old != null ; ) {
  351.         Entry e = old;
  352.         old = old.next;
  353.  
  354.         int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  355.         e.next = newMap[index];
  356.         newMap[index] = e;
  357.         }
  358.     }
  359.     }
  360.  
  361.     /**
  362.      * Maps the specified <code>key</code> to the specified 
  363.      * <code>value</code> in this hashtable. Neither the key nor the 
  364.      * value can be <code>null</code>. <p>
  365.      *
  366.      * The value can be retrieved by calling the <code>get</code> method 
  367.      * with a key that is equal to the original key. 
  368.      *
  369.      * @param      key     the hashtable key.
  370.      * @param      value   the value.
  371.      * @return     the previous value of the specified key in this hashtable,
  372.      *             or <code>null</code> if it did not have one.
  373.      * @exception  NullPointerException  if the key or value is
  374.      *               <code>null</code>.
  375.      * @see     Object#equals(Object)
  376.      * @see     #get(Object)
  377.      */
  378.     public synchronized Object put(Object key, Object value) {
  379.     // Make sure the value is not null
  380.     if (value == null) {
  381.         throw new NullPointerException();
  382.     }
  383.  
  384.     // Makes sure the key is not already in the hashtable.
  385.     Entry tab[] = table;
  386.     int hash = key.hashCode();
  387.     int index = (hash & 0x7FFFFFFF) % tab.length;
  388.     for (Entry e = tab[index] ; e != null ; e = e.next) {
  389.         if ((e.hash == hash) && e.key.equals(key)) {
  390.         Object old = e.value;
  391.         e.value = value;
  392.         return old;
  393.         }
  394.     }
  395.  
  396.     modCount++;
  397.     if (count >= threshold) {
  398.         // Rehash the table if the threshold is exceeded
  399.         rehash();
  400.  
  401.             tab = table;
  402.             index = (hash & 0x7FFFFFFF) % tab.length;
  403.     } 
  404.  
  405.     // Creates the new entry.
  406.     Entry e = new Entry(hash, key, value, tab[index]);
  407.     tab[index] = e;
  408.     count++;
  409.     return null;
  410.     }
  411.  
  412.     /**
  413.      * Removes the key (and its corresponding value) from this 
  414.      * hashtable. This method does nothing if the key is not in the hashtable.
  415.      *
  416.      * @param   key   the key that needs to be removed.
  417.      * @return  the value to which the key had been mapped in this hashtable,
  418.      *          or <code>null</code> if the key did not have a mapping.
  419.      */
  420.     public synchronized Object remove(Object key) {
  421.     Entry tab[] = table;
  422.     int hash = key.hashCode();
  423.     int index = (hash & 0x7FFFFFFF) % tab.length;
  424.     for (Entry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
  425.         if ((e.hash == hash) && e.key.equals(key)) {
  426.         modCount++;
  427.         if (prev != null) {
  428.             prev.next = e.next;
  429.         } else {
  430.             tab[index] = e.next;
  431.         }
  432.         count--;
  433.         Object oldValue = e.value;
  434.         e.value = null;
  435.         return oldValue;
  436.         }
  437.     }
  438.     return null;
  439.     }
  440.  
  441.     /**
  442.      * Copies all of the mappings from the specified Map to this Hashtable
  443.      * These mappings will replace any mappings that this Hashtable had for any
  444.      * of the keys currently in the specified Map. 
  445.      *
  446.      * @since JDK1.2
  447.      */
  448.     public synchronized void putAll(Map t) {
  449.     Iterator i = t.entrySet().iterator();
  450.     while (i.hasNext()) {
  451.         Map.Entry e = (Map.Entry) i.next();
  452.         put(e.getKey(), e.getValue());
  453.     }
  454.     }
  455.  
  456.     /**
  457.      * Clears this hashtable so that it contains no keys. 
  458.      */
  459.     public synchronized void clear() {
  460.     Entry tab[] = table;
  461.     modCount++;
  462.     for (int index = tab.length; --index >= 0; )
  463.         tab[index] = null;
  464.     count = 0;
  465.     }
  466.  
  467.     /**
  468.      * Creates a shallow copy of this hashtable. All the structure of the 
  469.      * hashtable itself is copied, but the keys and values are not cloned. 
  470.      * This is a relatively expensive operation.
  471.      *
  472.      * @return  a clone of the hashtable.
  473.      */
  474.     public synchronized Object clone() {
  475.     try { 
  476.         Hashtable t = (Hashtable)super.clone();
  477.         t.table = new Entry[table.length];
  478.         for (int i = table.length ; i-- > 0 ; ) {
  479.         t.table[i] = (table[i] != null) 
  480.             ? (Entry)table[i].clone() : null;
  481.         }
  482.         t.keySet = null;
  483.         t.entrySet = null;
  484.             t.values = null;
  485.         t.modCount = 0;
  486.         return t;
  487.     } catch (CloneNotSupportedException e) { 
  488.         // this shouldn't happen, since we are Cloneable
  489.         throw new InternalError();
  490.     }
  491.     }
  492.  
  493.     /**
  494.      * Returns a string representation of this <tt>Hashtable</tt> object 
  495.      * in the form of a set of entries, enclosed in braces and separated 
  496.      * by the ASCII characters "<tt>, </tt>" (comma and space). Each 
  497.      * entry is rendered as the key, an equals sign <tt>=</tt>, and the 
  498.      * associated element, where the <tt>toString</tt> method is used to 
  499.      * convert the key and element to strings. <p>Overrides to 
  500.      * <tt>toString</tt> method of <tt>Object</tt>.
  501.      *
  502.      * @return  a string representation of this hashtable.
  503.      */
  504.     public synchronized String toString() {
  505.     int max = size() - 1;
  506.     StringBuffer buf = new StringBuffer();
  507.     Iterator it = entrySet().iterator();
  508.  
  509.     buf.append("{");
  510.     for (int i = 0; i <= max; i++) {
  511.         Entry e = (Entry) (it.next());
  512.         buf.append(e.key + "=" + e.value);
  513.         if (i < max)
  514.         buf.append(", ");
  515.     }
  516.     buf.append("}");
  517.     return buf.toString();
  518.     }
  519.  
  520.  
  521.     // Views
  522.  
  523.     private transient Set keySet = null;
  524.     private transient Set entrySet = null;
  525.     private transient Collection values = null;
  526.  
  527.     /**
  528.      * Returns a Set view of the keys contained in this Hashtable.  The Set
  529.      * is backed by the Hashtable, so changes to the Hashtable are reflected
  530.      * in the Set, and vice-versa.  The Set supports element removal
  531.      * (which removes the corresponding entry from the Hashtable), but not
  532.      * element addition.
  533.      *
  534.      * @since JDK1.2
  535.      */
  536.     public Set keySet() {
  537.     if (keySet == null)
  538.         keySet = Collections.synchronizedSet(new KeySet(), this);
  539.     return keySet;
  540.     }
  541.  
  542.     private class KeySet extends AbstractSet {
  543.         public Iterator iterator() {
  544.             return new Enumerator(KEYS, true);
  545.         }
  546.         public int size() {
  547.             return count;
  548.         }
  549.         public boolean contains(Object o) {
  550.             return containsKey(o);
  551.         }
  552.         public boolean remove(Object o) {
  553.             return Hashtable.this.remove(o) != null;
  554.         }
  555.         public void clear() {
  556.             Hashtable.this.clear();
  557.         }
  558.     }
  559.  
  560.     /**
  561.      * Returns a Set view of the entries contained in this Hashtable.
  562.      * Each element in this collection is a Map.Entry.  The Set is
  563.      * backed by the Hashtable, so changes to the Hashtable are reflected in
  564.      * the Set, and vice-versa.  The Set supports element removal
  565.      * (which removes the corresponding entry from the Hashtable),
  566.      * but not element addition.
  567.      *
  568.      * @see   Map.Entry
  569.      * @since JDK1.2
  570.      */
  571.     public Set entrySet() {
  572.     if (entrySet==null)
  573.         entrySet = Collections.synchronizedSet(new EntrySet(), this);
  574.     return entrySet;
  575.     }
  576.  
  577.     private class EntrySet extends AbstractSet {
  578.         public Iterator iterator() {
  579.             return new Enumerator(ENTRIES, true);
  580.         }
  581.  
  582.         public boolean contains(Object o) {
  583.             if (!(o instanceof Map.Entry))
  584.                 return false;
  585.             Map.Entry entry = (Map.Entry)o;
  586.             Object key = entry.getKey();
  587.             Entry tab[] = table;
  588.             int hash = key.hashCode();
  589.             int index = (hash & 0x7FFFFFFF) % tab.length;
  590.  
  591.             for (Entry e = tab[index]; e != null; e = e.next)
  592.                 if (e.hash==hash && e.equals(entry))
  593.                     return true;
  594.             return false;
  595.         }
  596.  
  597.         public boolean remove(Object o) {
  598.             if (!(o instanceof Map.Entry))
  599.                 return false;
  600.             Map.Entry entry = (Map.Entry)o;
  601.             Object key = entry.getKey();
  602.             Entry tab[] = table;
  603.             int hash = key.hashCode();
  604.             int index = (hash & 0x7FFFFFFF) % tab.length;
  605.  
  606.             for (Entry e = tab[index], prev = null; e != null;
  607.                  prev = e, e = e.next) {
  608.                 if (e.hash==hash && e.equals(entry)) {
  609.                     modCount++;
  610.                     if (prev != null)
  611.                         prev.next = e.next;
  612.                     else
  613.                         tab[index] = e.next;
  614.  
  615.                     count--;
  616.                     e.value = null;
  617.                     return true;
  618.                 }
  619.             }
  620.             return false;
  621.         }
  622.  
  623.         public int size() {
  624.             return count;
  625.         }
  626.  
  627.         public void clear() {
  628.             Hashtable.this.clear();
  629.         }
  630.     }
  631.  
  632.     /**
  633.      * Returns a Collection view of the values contained in this Hashtable.
  634.      * The Collection is backed by the Hashtable, so changes to the Hashtable
  635.      * are reflected in the Collection, and vice-versa.  The Collection
  636.      * supports element removal (which removes the corresponding entry from
  637.      * the Hashtable), but not element addition.
  638.      *
  639.      * @since JDK1.2
  640.      */
  641.     public Collection values() {
  642.     if (values==null)
  643.         values = Collections.synchronizedCollection(new ValueCollection(),
  644.                                                         this);
  645.         return values;
  646.     }
  647.  
  648.     private class ValueCollection extends AbstractCollection {
  649.         public Iterator iterator() {
  650.             return new Enumerator(VALUES, true);
  651.         }
  652.         public int size() {
  653.             return count;
  654.         }
  655.         public boolean contains(Object o) {
  656.             return containsValue(o);
  657.         }
  658.         public void clear() {
  659.             Hashtable.this.clear();
  660.         }
  661.     }
  662.  
  663.     // Comparison and hashing
  664.  
  665.     /**
  666.      * Compares the specified Object with this Map for equality,
  667.      * as per the definition in the Map interface.
  668.      *
  669.      * @return true if the specified Object is equal to this Map.
  670.      * @see Map#equals(Object)
  671.      * @since JDK1.2
  672.      */
  673.     public synchronized boolean equals(Object o) {
  674.     if (o == this)
  675.         return true;
  676.  
  677.     if (!(o instanceof Map))
  678.         return false;
  679.     Map t = (Map) o;
  680.     if (t.size() != size())
  681.         return false;
  682.  
  683.     Iterator i = entrySet().iterator();
  684.     while (i.hasNext()) {
  685.         Entry e = (Entry) i.next();
  686.         Object key = e.getKey();
  687.         Object value = e.getValue();
  688.         if (value == null) {
  689.         if (!(t.get(key)==null && t.containsKey(key)))
  690.             return false;
  691.         } else {
  692.         if (!value.equals(t.get(key)))
  693.             return false;
  694.         }
  695.     }
  696.     return true;
  697.     }
  698.  
  699.     /**
  700.      * Returns the hash code value for this Map as per the definition in the
  701.      * Map interface.
  702.      *
  703.      * @see Map#hashCode()
  704.      * @since JDK1.2
  705.      */
  706.     public synchronized int hashCode() {
  707.     int h = 0;
  708.     Iterator i = entrySet().iterator();
  709.     while (i.hasNext())
  710.         h += i.next().hashCode();
  711.     return h;
  712.     }
  713.  
  714.     /**
  715.      * Save the state of the Hashtable to a stream (i.e., serialize it).
  716.      *
  717.      * @serialData The <i>capacity</i> of the Hashtable (the length of the
  718.      *           bucket array) is emitted (int), followed  by the
  719.      *           <i>size</i> of the Hashtable (the number of key-value
  720.      *           mappings), followed by the key (Object) and value (Object)
  721.      *           for each key-value mapping represented by the Hashtable
  722.      *           The key-value mappings are emitted in no particular order.
  723.      */
  724.     private synchronized void writeObject(java.io.ObjectOutputStream s)
  725.         throws IOException
  726.     {
  727.     // Write out the length, threshold, loadfactor
  728.     s.defaultWriteObject();
  729.  
  730.     // Write out length, count of elements and then the key/value objects
  731.     s.writeInt(table.length);
  732.     s.writeInt(count);
  733.     for (int index = table.length-1; index >= 0; index--) {
  734.         Entry entry = table[index];
  735.  
  736.         while (entry != null) {
  737.         s.writeObject(entry.key);
  738.         s.writeObject(entry.value);
  739.         entry = entry.next;
  740.         }
  741.     }
  742.     }
  743.  
  744.     /**
  745.      * Reconstitute the Hashtable from a stream (i.e., deserialize it).
  746.      */
  747.     private synchronized void readObject(java.io.ObjectInputStream s)
  748.          throws IOException, ClassNotFoundException
  749.     {
  750.     // Read in the length, threshold, and loadfactor
  751.     s.defaultReadObject();
  752.  
  753.     // Read the original length of the array and number of elements
  754.     int origlength = s.readInt();
  755.     int elements = s.readInt();
  756.  
  757.     // Compute new size with a bit of room 5% to grow but
  758.     // No larger than the original size.  Make the length
  759.     // odd if it's large enough, this helps distribute the entries.
  760.     // Guard against the length ending up zero, that's not valid.
  761.     int length = (int)(elements * loadFactor) + (elements / 20) + 3;
  762.     if (length > elements && (length & 1) == 0)
  763.         length--;
  764.     if (origlength > 0 && length > origlength)
  765.         length = origlength;
  766.  
  767.     table = new Entry[length];
  768.     count = 0;
  769.  
  770.     // Read the number of elements and then all the key/value objects
  771.     for (; elements > 0; elements--) {
  772.         Object key = s.readObject();
  773.         Object value = s.readObject();
  774.         put(key, value);
  775.     }
  776.     }
  777.  
  778.  
  779.     /**
  780.      * Hashtable collision list.
  781.      */
  782.     private static class Entry implements Map.Entry {
  783.     int hash;
  784.     Object key;
  785.     Object value;
  786.     Entry next;
  787.  
  788.     protected Entry(int hash, Object key, Object value, Entry next) {
  789.         this.hash = hash;
  790.         this.key = key;
  791.         this.value = value;
  792.         this.next = next;
  793.     }
  794.  
  795.     protected Object clone() {
  796.         return new Entry(hash, key, value,
  797.                  (next==null ? null : (Entry)next.clone()));
  798.     }
  799.  
  800.     // Map.Entry Ops 
  801.  
  802.     public Object getKey() {
  803.         return key;
  804.     }
  805.  
  806.     public Object getValue() {
  807.         return value;
  808.     }
  809.  
  810.     public Object setValue(Object value) {
  811.         if (value == null)
  812.         throw new NullPointerException();
  813.  
  814.         Object oldValue = this.value;
  815.         this.value = value;
  816.         return oldValue;
  817.     }
  818.  
  819.     public boolean equals(Object o) {
  820.         if (!(o instanceof Map.Entry))
  821.         return false;
  822.         Map.Entry e = (Map.Entry)o;
  823.  
  824.         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  825.            (value==null ? e.getValue()==null : value.equals(e.getValue()));
  826.     }
  827.  
  828.     public int hashCode() {
  829.         return hash ^ (value==null ? 0 : value.hashCode());
  830.     }
  831.  
  832.     public String toString() {
  833.         return key.toString()+"="+value.toString();
  834.     }
  835.     }
  836.  
  837.     // Types of Enumerations/Iterations
  838.     private static final int KEYS = 0;
  839.     private static final int VALUES = 1;
  840.     private static final int ENTRIES = 2;
  841.  
  842.     /**
  843.      * A hashtable enumerator class.  This class implements both the
  844.      * Enumeration and Iterator interfaces, but individual instances
  845.      * can be created with the Iterator methods disabled.  This is necessary
  846.      * to avoid unintentionally increasing the capabilities granted a user
  847.      * by passing an Enumeration.
  848.      */
  849.     private class Enumerator implements Enumeration, Iterator {
  850.     Entry[] table = Hashtable.this.table;
  851.     int index = table.length;
  852.     Entry entry = null;
  853.     Entry lastReturned = null;
  854.     int type;
  855.  
  856.     /**
  857.      * Indicates whether this Enumerator is serving as an Iterator
  858.      * or an Enumeration.  (true -> Iterator).
  859.      */
  860.     boolean iterator;
  861.  
  862.     /**
  863.      * The modCount value that the iterator believes that the backing
  864.      * List should have.  If this expectation is violated, the iterator
  865.      * has detected concurrent modification.
  866.      */
  867.     private int expectedModCount = modCount;
  868.  
  869.     Enumerator(int type, boolean iterator) {
  870.         this.type = type;
  871.         this.iterator = iterator;
  872.     }
  873.  
  874.     public boolean hasMoreElements() {
  875.         while (entry==null && index>0)
  876.         entry = table[--index];
  877.  
  878.         return entry != null;
  879.     }
  880.  
  881.     public Object nextElement() {
  882.         while (entry==null && index>0)
  883.         entry = table[--index];
  884.  
  885.         if (entry != null) {
  886.         Entry e = lastReturned = entry;
  887.         entry = e.next;
  888.         return type == KEYS ? e.key : (type == VALUES ? e.value : e);
  889.         }
  890.         throw new NoSuchElementException("Hashtable Enumerator");
  891.     }
  892.  
  893.     // Iterator methods
  894.     public boolean hasNext() {
  895.         return hasMoreElements();
  896.     }
  897.  
  898.     public Object next() {
  899.         if (modCount != expectedModCount)
  900.         throw new ConcurrentModificationException();
  901.         return nextElement();
  902.     }
  903.  
  904.     public void remove() {
  905.         if (!iterator)
  906.         throw new UnsupportedOperationException();
  907.         if (lastReturned == null)
  908.         throw new IllegalStateException("Hashtable Enumerator");
  909.         if (modCount != expectedModCount)
  910.         throw new ConcurrentModificationException();
  911.  
  912.         synchronized(Hashtable.this) {
  913.         Entry[] tab = Hashtable.this.table;
  914.         int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
  915.  
  916.         for (Entry e = tab[index], prev = null; e != null;
  917.              prev = e, e = e.next) {
  918.             if (e == lastReturned) {
  919.             modCount++;
  920.             expectedModCount++;
  921.             if (prev == null)
  922.                 tab[index] = e.next;
  923.             else
  924.                 prev.next = e.next;
  925.             count--;
  926.             lastReturned = null;
  927.             return;
  928.             }
  929.         }
  930.         throw new ConcurrentModificationException();
  931.         }
  932.     }
  933.     }
  934. }
  935.